#include "pli.h"


/* Funcao que apaga os arquivos gerados */
void deleteFiles() {
    char command[MAX_COMM_LENGTH];

    sprintf(command, "rm -rf %s %s %s %s %s",
            PLI_MODEL_PATH, PLI_DATA_PATH, PLI_SOL_PATH,
            PLI_DISP_PATH, PLI_LOG_PATH);

    if (DEBUG) {
        fprintf(stderr, "comando gerado para remocao: %s\n\n", command);
    }

    execComm(command);
}


/* Funcao que gera o arquivo do modelo PLI em GMPL */
void genModFile() {
    FILE *arq;

    arq = openFile(PLI_MODEL_PATH, "w");

    /* descricao */
    fprintf(arq, "# SUM-FINISHING-TIME PROBLEM\n#\n");
    fprintf(arq, "# Descricao do problema nas paginas 444-448 do livro\n");
    fprintf(arq, "# \"Combinatorial Optimization\" de Papadimitriou e\n");
    fprintf(arq, "# Steiglitz, Prentice-Hall INC., 182\n\n");

    /* parametros */
    fprintf(arq, "/* Conjunto de jobs */\n");
    fprintf(arq, "set JOB;\n\n");
    fprintf(arq, "/* Parametros */\n");
    fprintf(arq, "param task1 {j in JOB};\n");
    fprintf(arq, "param task2 {j in JOB};\n\n");
    fprintf(arq, "param totalmax := sum{j in JOB} (task1[j]+task2[j]);\n\n");

    /* variaveis */
    fprintf(arq, "/* Variaveis de decisao */\n");
    fprintf(arq, "var start  {j in JOB} >= 0, integer;");
    fprintf(arq, " # inicio de execucao na maquina 1\n");

    fprintf(arq, "var finish {j in JOB} >= 0, integer;");
    fprintf(arq, " # termino de execucao na maquina 2\n\n");

    fprintf(arq, "var delta  {i in JOB, k in JOB} >= 0, integer;");
    fprintf(arq, " # variavel auxiliar na ordem\n\n");

    /* funcao objetivo */
    fprintf(arq, "/* Funcao objetivo */\n");
    fprintf(arq, "minimize sft: sum{j in JOB} finish[j]; # sum-finishing-time\n\n\n");

    /* restricoes */
    fprintf(arq, "/* Restricoes */\n");
    fprintf(arq, "s.t. order    {i in JOB, k in JOB: i<k }:\n");
    fprintf(arq, "\t\t\tdelta[i,k] + delta[k,i] = 1;\n\n");

    fprintf(arq, "s.t. starting {i in JOB, k in JOB: i!=k}:\n");
    fprintf(arq, "\t\t\tdelta[i,k]*totalmax + start[i] >= start[k] + task1[k];\n\n");

    fprintf(arq, "s.t. finishing1 {i in JOB, k in JOB: i!=k}:\n");
    fprintf(arq, "\t\t\tdelta[i,k]*totalmax + finish[i] >= finish[k] + task2[i];\n\n");

    fprintf(arq, "s.t. finishing2 {i in JOB}:\n");
    fprintf(arq, "\t\t\tfinish[i] >= start[i] + task1[i] + task2[i];\n\n\n\n");

    /* saida (para facilitar o parse) */
    fprintf(arq, "solve;\n\n");
    fprintf(arq, "printf \"%%d \", sum{i in JOB} finish[i];\n");
    fprintf(arq, "for {i in JOB}\n");
    fprintf(arq, "\tprintf \"%%d \", start[i];\n\n\n\n");

    fprintf(arq, "end;\n");

    fclose(arq);
}


/* Funcao que gera o arquivo de dados */
void genDataFile(const Job *jobs, const int nJobs) {
    FILE *arq;
    int i;

    arq = openFile(PLI_DATA_PATH, "w");
    fprintf(arq, "# Arquivo de dados para o modelo\n");
    fprintf(arq, "# descrito em %s\n\n", PLI_MODEL_PATH);
    fprintf(arq, "data;\n\n");

    /* set JOB */
    fprintf(arq, "set JOB :=");
    for (i = 0; i < nJobs; i++) {
        fprintf(arq, " %d", i+1);
    }
    fprintf(arq, ";\n\n");

    /* param task1 */
    fprintf(arq, "param task1 :=");
    for (i = 0; i < nJobs; i++) {
        fprintf(arq, "\n\t\t%d\t%d", i+1, jobs[i].task1);
    }
    fprintf(arq, ";\n\n");

    /* param task2 */
    fprintf(arq, "param task2 :=");
    for (i = 0; i < nJobs; i++) {
        fprintf(arq, "\n\t\t%d\t%d", i+1, jobs[i].task2);
    }
    fprintf(arq, ";\n\n");

    fprintf(arq, "end;\n");
    fclose(arq);
}


/* Funcao que gera o arquivo de solucao */
void genSolFile(const int maxExecTime, const int maxNodes) {
    char command[MAX_COMM_LENGTH];

    sprintf(command, "glpsol --math --model %s --data %s --output %s \
            --memlim %d --tmlim %d --display %s --log %s",
            PLI_MODEL_PATH, PLI_DATA_PATH, PLI_SOL_PATH,
            (int)ceil(maxNodes * MB_PER_NODE), maxExecTime,
            PLI_DISP_PATH, PLI_LOG_PATH);

    fprintf(stderr, "comando gerado para solucao: %s\n\n", command);

    execComm(command);
}


/* Funcao que imprime a solucao encontrada */
void printSol(const int nJobs) {
    FILE *arq;
    int i, sft;
    Disp *dispOrd;

    arq = openFile(PLI_DISP_PATH, "r");
    dispOrd = memAlloc(sizeof(Disp)*nJobs);

    /* monta a solucao encontrada */
    fscanf(arq, "%d", &sft);
    if (sft != 0) {
        for (i = 0; i < nJobs; i++) {
            dispOrd[i].jobID = i+1;
            fscanf(arq, "%d", &dispOrd[i].start);
        }
        qsort(dispOrd, nJobs, sizeof(Disp), (int(*)(const void*, const void*))dispcmp);

        /* imprime a saida */
        printf("\n> melhor solucao encontrada: ");
        for (i = 0; i < nJobs; i++) {
            printf("%d ", dispOrd[i].jobID);
        }
        printf("(%d)\n", sft);
    } else {
        printf("\n> nenhuma solucao encontrada\n");
    }
    fclose(arq);
    free(dispOrd);
}


/* Funcao principal */
int main(int argc, char *argv[]) {

    int maxExecTime, maxNodes;
    FILE *param, *inst;
    Job *jobs;
    int nJobs, i;


    /* verifica o uso */
    if (argc != 2) {
        fprintf(stderr, "uso: bnb arq_instancia\n");
        exit(EXIT_FAILURE);
    }

    /* le o arquivo param */
    param = openFile(PARAM_PATH, "r");
    fscanf(param, "%d %d", &maxNodes, &maxExecTime);
    fclose(param);

    /* le a entrada */
    inst = openFile(argv[1], "r");
    fscanf(inst, "%d", &nJobs);
    jobs = (Job*)memAlloc(sizeof(Job) * nJobs);
    for (i = 0; i < nJobs; i++) {
        jobs[i].id = i;
        fscanf(inst, "%d %d", &jobs[i].task1, &jobs[i].task2);
    }
    fclose(inst);


    /* DEBUG: imprime a entrada */
    if (DEBUG) {
        fprintf(stderr, "entrada:\n");
        for (i = 0; i < nJobs; i++) {
            fprintf(stderr, "job%d: %d %d\n", jobs[i].id+1, jobs[i].task1, jobs[i].task2);
        }
        fprintf(stderr, "\n");
    }
    
    
    /* construcao do modelo PLI (gmpl) */
    genModFile();
    genDataFile(jobs, nJobs);

    /* resolucao do FSP por PLI (glpk) */
    genSolFile(maxExecTime, maxNodes);

    /* imprime a saida */
    printSol(nJobs);


    /* apaga os arquivos gerados */
    if (DELETE_PLI_FILES) {
        deleteFiles();
    }

    /* libera a memoria */
    free(jobs);

    return EXIT_SUCCESS;

}

